#include <iostream>
#include <sstream>

#include <typeinfo>


#include <cppunit/TestCase.h>
#include <cppunit/TestFixture.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestSuite.h>

#include <cppunit/ui/text/TestRunner.h>

#include "rep.hpp"


using namespace std;
using namespace rep;

class AllocationTest : public CppUnit::TestFixture{
public:
  void setUp(){
    //initialize with 16 slots
    FrameAllocator::init(4);
  }
  void tearDown(){
    FrameAllocator::done();
  }
protected:

  void testDummy(){}

  void testAllocate1(){
    Slot* foo = FrameAllocator::getFrameWeak(1);
    CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", foo!=NULL);
  }

  void testAllocate16(){
    int i;
    Slot* slots[16];
    for(i = 0; i < 16; i++){
      slots[i] = FrameAllocator::getFrameWeak(1);
      CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", slots[i] !=NULL);
    }
    //Check to make sure we're not crossing wires here.
    for(i = 0; i < 16; i++){
      slots[i]->i = i;
    }
    for(i = 0; i < 16; i++){
      CPPUNIT_ASSERT_EQUAL(i, slots[i]->i);
    }
    //Make sure we're actually not allocating too little or something...
    Slot* not_gonna_work = FrameAllocator::getFrameWeak(1);
    CPPUNIT_ASSERT_MESSAGE("We should be out of slots", not_gonna_work == NULL);
  }

  void testAllocate4bigger(){
    int i;
    Slot* slots[4];
    for(i = 0; i < 4; i++){
      slots[i] = FrameAllocator::getFrameWeak(4);
      CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", slots[i] !=NULL);
    }
    //Check to make sure we're not crossing wires here.
    for(i = 0; i < 4; i++){
      slots[i][0].i = i;
    }
    for(i = 0; i < 4; i++){
      CPPUNIT_ASSERT_EQUAL(i, slots[i][0].i);
    }
    //Make sure we're actually not allocating too little or something...
    Slot* not_gonna_work = FrameAllocator::getFrameWeak(1);
    CPPUNIT_ASSERT_MESSAGE("We should be out of slots", not_gonna_work == NULL);
  }

  void testAllocate4sz3(){
    int i;
    Slot* slots[4];
    for(i = 0; i < 4; i++){
      slots[i] = FrameAllocator::getFrameWeak(3);
      CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", slots[i] !=NULL);
    }
    //Check to make sure we're not crossing wires here.
    for(i = 0; i < 4; i++){
      slots[i][0].i = i;
    }
    for(i = 0; i < 4; i++){
      CPPUNIT_ASSERT_EQUAL(i, slots[i][0].i);
    }
    //Make sure we're actually not allocating too little or something...
    Slot* not_gonna_work = FrameAllocator::getFrameWeak(1);
    CPPUNIT_ASSERT_MESSAGE("We should be out of slots", not_gonna_work == NULL);
  }

  void testAllocateDifferentSizes(){
    int i;
    Slot* slots[5];

    slots[0] = FrameAllocator::getFrameWeak(1);
    CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", slots[0] !=NULL);
    slots[1] = FrameAllocator::getFrameWeak(4);
    CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", slots[1] !=NULL);
    slots[2] = FrameAllocator::getFrameWeak(2);
    CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", slots[2] !=NULL);
    slots[3] = FrameAllocator::getFrameWeak(8);
    CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", slots[3] !=NULL);
    slots[4] = FrameAllocator::getFrameWeak(1);
    CPPUNIT_ASSERT_MESSAGE("The alllocated slot should not be null", slots[4] !=NULL);

    //Check to make sure we're not crossing wires here.
    for(i = 0; i < 5; i++){
      slots[i][0].i = i;
    }
    for(i = 0; i < 5; i++){
      CPPUNIT_ASSERT_EQUAL(i, slots[i][0].i);
    }
    //Make sure we're actually not allocating too little or something...
    Slot* not_gonna_work = FrameAllocator::getFrameWeak(1);
    CPPUNIT_ASSERT_MESSAGE("We should be out of slots", not_gonna_work == NULL);
  }


public:

  static CppUnit::Test* suite(){
    CppUnit::TestSuite *suite = new CppUnit::TestSuite("AllocationTest");
    suite->addTest( new CppUnit::TestCaller<AllocationTest>("testAllocate1", 
                                                             &AllocationTest::testAllocate1));
    suite->addTest( new CppUnit::TestCaller<AllocationTest>("testAllocate16", 
                                                             &AllocationTest::testAllocate16));
    suite->addTest( new CppUnit::TestCaller<AllocationTest>("testAllocate4bigger", 
                                                             &AllocationTest::testAllocate4bigger));
    suite->addTest( new CppUnit::TestCaller<AllocationTest>("testAllocate4sz3", 
                                                            &AllocationTest::testAllocate4sz3));
    suite->addTest( new CppUnit::TestCaller<AllocationTest>("testAllocateDifferentSizes", 
                                                            &AllocationTest::testAllocateDifferentSizes));
    return suite;
  }
};
